#ifndef ASIO_BOX_CONNECTION_IMPL_H_INCLUDED
#define ASIO_BOX_CONNECTION_IMPL_H_INCLUDED

#include <Core/SyncedCommandIOStream.h>
#include <Core/BoxApi/IBoxConnection.h>

#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>

namespace Core { namespace BoxApi {

namespace Aux {

/**
 * Shared Asio io service ref, 
 * follow solution is better when create service each connection
 */
boost::shared_ptr<boost::asio::io_service> getSharedIOService();

/**
 * Synced stream implemntation(Asio sockets)
 */
class AsioSyncedCommandIOStream : public SyncedCommandIOStream {
public :
	explicit AsioSyncedCommandIOStream(boost::asio::io_service & ioServiceRef) : 
		SyncedCommandIOStream(), 
		m_socket(ioServiceRef) 
	{ 
	}
	
	virtual ~AsioSyncedCommandIOStream() 
		{ close(); }
	
	virtual std::size_t write(std::vector<boost::uint8_t> const & inBytes) 
		{ return boost::asio::write(m_socket, boost::asio::buffer(inBytes, inBytes.size())); }

	virtual std::size_t read(std::vector<boost::uint8_t> & outBytes) 
		{ return boost::asio::read(m_socket, boost::asio::buffer(outBytes, outBytes.size())); }

	virtual void close() 
		{ m_socket.close(); }
	
	virtual std::size_t readAtLeast(std::vector<boost::uint8_t> & outBytes, std::size_t buffPos, std::size_t atLeast) 
	{
		std::size_t const readed = boost::asio::read(m_socket, 
				boost::asio::buffer(&outBytes.at(buffPos), atLeast),
				boost::asio::transfer_at_least(atLeast));
		return readed;
	}

	inline boost::asio::ip::tcp::socket & getSocket() 
		{ return m_socket; }

private :
	boost::asio::ip::tcp::socket m_socket;
};

} // namespace Aux

/**
 * AsioBoxConnectionImpl
 * TODO add thread safe via cond. vars
 */
class AsioBoxConnectionImpl : public IBoxConnection {
public :
	explicit AsioBoxConnectionImpl(BoxConnectionParams const & params);
	~AsioBoxConnectionImpl() throw ();

	virtual void establishConnection() throw (std::exception);
	virtual void close() throw ();
	
	virtual int getConnectionId() const throw (std::exception);
	virtual ICommandPtr sendMessageSync(ICommandPtr command);
	
	virtual std::size_t writeRawData(std::vector<boost::uint8_t> const & data) throw();

private :
	BoxConnectionParams const m_params;
	boost::shared_ptr<Aux::AsioSyncedCommandIOStream> m_commandStream;

};

typedef boost::shared_ptr<AsioBoxConnectionImpl> AsioBoxConnectionImplPtr;

} } // namespace Core, BoxApi

#endif

